{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# python程序名字作用域\n",
    "*hawksoft邢永康*（WeChat：13320218299）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "在python中，名字（Name）是使用对象的唯一的手段。一个对象一旦被剥夺了名字，它就无法被使用，从而就没有存在的价值，也就会被从这个世界消除。因此，在python中，如果要使用一个对象，首先要获得该对象的名字。而名字作用域，指的就是一个名字的有效范围。根据不同的程序结构，名字的作用范围也不同。\n",
    "\n",
    "一个python项目的代码结构大致如下：\n",
    "\n",
    "- 一到多个module（每个文件是一个module）\n",
    "- 每个module中包含零到多个函数，以及零到多条非函数语句\n",
    "\n",
    "因此，根据该结构，我们需要研究两种情况下的名字的作用域：\n",
    "- 模块间的名字作用域。\n",
    "- 模块内的名字的作用域。\n",
    "\n",
    "## 1 模块之间的名字作用域\n",
    "\n",
    "不同模块之间的名字作用与比较简单，基本原则如下：\n",
    "\n",
    "1. 一个模块内的名字在另一个模块不不能直接可见。\n",
    "2. 要使用另一个模块内的名字（当然是为了使用那个名字对应的对象），必须使用import语句。\n",
    "\n",
    "如下面的代码所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#m1.py\n",
    "m1_a = 0\n",
    "def m1_f1(p1):\n",
    "    b =  p1 * 2\n",
    "    return b\n",
    "def m1_f2(p2):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "20\n"
     ]
    }
   ],
   "source": [
    "# m2.py\n",
    "import m1         # 导入m1模块\n",
    "print(m1.m1_a)       # 使用m1模块中的m1_a名字的对象\n",
    "print(m1.m1_f1(10)) # 使用m1模块中的m1_f1名字的对象"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这种访问名字的方式，称为名字空间。它是一种层次化的结构。后面我们还要继续讲解。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 模块内名字的作用域\n",
    "\n",
    "模块内的名字，根据其定义的位置，可以分为两类：\n",
    "\n",
    "1. 定义在function函数块内的名字，称为本地名字（local name）。\n",
    "2. 定义在module模块中的名字，成为全局名字（global name）。\n",
    "\n",
    "那么什么叫名字的定义呢？在python中，大概有三种情况：\n",
    "1. 在模块中，一个名字**第一次**出现在等号的左边，或出现在def后面，此时定义了一个全局名字。\n",
    "2. 在函数中，一个名字**第一次**出现在等号的左边，或出现在def后面，此时定义了一个局部名字。\n",
    "3. 一个名字出现在**函数的参数**中，此时定义了一个局部名字。\n",
    "\n",
    "理解这个，关键是要认识到：定义函数的语句，也是在创建对象并给对象赋予名字，它与我门常用的$a=1$这样的语句没有什么区别。尤其是学过c或java的同学，其实内心深处认为函数很高级。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n"
     ]
    }
   ],
   "source": [
    "a = 10\n",
    "def fun1(p1):\n",
    "    d = 10\n",
    "    b = d * p1\n",
    "    return b\n",
    "c = fun1(a)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "分析如下:\n",
    "- a、c和fun1都是global name;\n",
    "- b和d是local name\n",
    "- p1是一个local name\n",
    "- 每一名字后面都有一个对象：对象20的local name是b，但global name是c。\n",
    "- 对象d在函数调用结束后，名字被删除，所以对象10也被删除。\n",
    "- 对象20，尽管在函数调用结束后，名字b被删除，但对象被函数返回，并得到了新名字c。\n",
    "- 名字分为local和global，但对象只存在出生地不同（函数内或函数外），但不存在这种区分\n",
    "\n",
    "\n",
    "\n",
    "### 2.1 名字作用域基本原则 \n",
    "\n",
    "- 函数内部可以使用模块内前面定义的外部的名字，即可以访问其对应的对象，但无法改变该对象。\n",
    "\n",
    "- 模块中的语句，无法使用函数中定义的名字。\n",
    "\n",
    "- 一个函数如果要将自己创建的对象传递到外边，有两种方法：\n",
    "\n",
    "- - return ：可以传递对象，但不能传递名字。\n",
    "\n",
    "  - global：传递名字给外边。 \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "30\n"
     ]
    }
   ],
   "source": [
    "a = 10\n",
    "def fun1():\n",
    "    a = 20\n",
    "    d = 10\n",
    "    b = a + d\n",
    "    return b\n",
    "c = fun1()\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 2.2 作用域冲突\n",
    "是指统一个名字，在不同的位置被定义。这些情况并不一定是错误的，但有些情况在编码是一定要避免。\n",
    "#### case 1:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "a = 1\n",
    "def f1():\n",
    "    a = 2\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个其实不算冲突。尽管是同一个名字，但前一个名字是全局名字，而后一个则是局部名字。后面的print(a)其实访问的是1这个对象。a=2定义了一个局部名字，两者没哟ren h"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
